use std::sync::Arc;

use anyhow::anyhow;
use axum::{Json, Router};
use axum::extract::{Query, State};
use axum::http::{HeaderMap, StatusCode};
use axum::routing::{delete, get, post};
use serde::Deserialize;
use serde_json::{from_value, json, Value};
use tokio::sync::Mutex;
use tracing::info;

use crate::app_state::AppState;
use crate::controller::point_controller;
use crate::dto::point_dto::{BasePointDto, IdParam, InterpolationQueryParams, InterpolationQueryResult, PointAddVal, PointDto, PointQuery, TDSQLQuery};
use crate::util::str_util::to_table_name;
use crate::util_entity::anyhow_res::{json_success_response, JsonResult};
use crate::util_entity::response::{json_failed, json_success, success};

pub fn get_machine_index(headers: HeaderMap) -> u32 {
    headers.get("Machineindex").unwrap().to_str().unwrap().parse::<u32>().unwrap()
}

pub fn route() -> Router<Arc<Mutex<AppState>>> {
    Router::new()
        .route("/GetAllPoints", get(get_all_points))
        .route("/GetPointsByIds", post(get_points_by_ids))
        .route("/GetPointsByNamesIgnoreCase", post(get_points_by_names_ignore_case))
        .route("/GetTdTables", post(get_td_tables))
        .route("/GetInterpolationSimple", post(get_interpolation_simple))
        .route("/AddPointVal", post(add_point_val))
        .route("/UpdatePoint", post(update_point))
        .route("/DeletePointById", delete(delete_point_by_id))
        .route("/GetPointTablePage", get(get_point_table_page))
}


pub async fn get_all_points(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Query(param): Query<PointQuery>,
) -> JsonResult<Value> {
    let points = app_state.lock().await.point_repo.get_points_async(get_machine_index(headers), param).await?;
    Ok(json_success_response(points))
}


fn parse_payload_to_vec_i32(payload: Value) -> Result<Vec<i32>, String> {
    let array = payload.as_array().ok_or("body 必须是数组才行")?;
    let array = array.iter().filter_map(|item| {
        item.as_i64().map(|s| s as i32)
            .or_else(|| item.as_str()?.parse::<i32>().ok())
    }).collect();
    Ok(array)
}

fn parse_payload_to_vec_string(payload: Value) -> Result<Vec<String>, String> {
    let array = payload.as_array().ok_or("body 必须是数组才行")?;
    let array = array.iter().filter_map(|item| {
        item.as_str().map(|s| s.to_string())
    }).collect();
    Ok(array)
}


pub async fn get_points_by_ids(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param = match parse_payload_to_vec_i32(payload) {
        Ok(param) => param,
        Err(err) => return Err(err.into()),
    };
    info!("machine_index : {}, param : {:?}", machine_index, param);
    let points = app_state.lock().await.point_repo.get_points_by_ids_async(machine_index, param).await?;
    Ok(json_success_response(points))
}

pub async fn get_points_by_names_ignore_case(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param = parse_payload_to_vec_string(payload)?;
    info!("machine_index : {}, param : {:?}", machine_index, param);
    let points = app_state.lock().await.point_repo.get_points_by_names_ignore_case(machine_index, param).await?;
    Ok(json_success_response(points))
}


pub async fn get_td_tables(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param = TDSQLQuery::from_json(payload)?;

    info!("machine_index : {}, ", machine_index);
    let points = app_state.lock().await.td_engine_repo.query(machine_index, param.sql).await?;
    Ok(json_success_response(points))
}

pub async fn get_interpolation_simple(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param: InterpolationQueryParams = from_value(payload)?;

    let futures = param.tableNames.iter().map(|table_name| {
        let table_name = table_name.clone();
        let machine_index = machine_index.clone();
        let param = param.clone();
        let sql = format!("select _wstart as ts , first(pvalue) as pvalue from `{}` where ts between '{}' and '{}' interval ({}) fill(prev)", table_name, param.startTime, param.endTime, param.interval);
        let app_state = Arc::clone(&app_state);
        async move {
            let ret = app_state.lock().await.td_engine_repo.query(machine_index, sql).await
                .map_err(|err| format!("查询失败: {}", err))?;
            // ret map to vec of (ts, pvalue)
            let ret = ret.iter().map(|item| {
                let ts = item.get("ts").unwrap().as_i64().unwrap();
                let pvalue = match item.get("pvalue") {
                    Some(pvalue) => pvalue.as_f64(),
                    None => None,
                };
                (ts, pvalue)
            }).collect::<Vec<_>>();
            let entity = InterpolationQueryResult {
                name: table_name,
                data: ret,
            };
            return Ok(entity);
        }
    }).collect::<Vec<_>>();
    let results: Vec<Result<InterpolationQueryResult, String>> = futures::future::join_all(futures).await;
    // 检查是否有任何查询失败
    for result in &results {
        if result.is_err() {
            return Err(result.as_ref().unwrap_err().to_string().into());
        }
    }
    // 如果所有查询都成功，获取结果值
    let values: Vec<_> = results.into_iter().map(|result| result.unwrap()).collect();
    return Ok(json_success_response(values));
}


pub async fn add_point_val(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param: PointAddVal = from_value(payload)?;
    // 先判断 data.Name 对应的td 表名是否存在
    let query = format!("describe `{}`", param.name);
    let _ = app_state.lock().await.td_engine_repo.query(machine_index, query).await?;
    // 插入数据
    // query = $"insert into {data.Name.ToTableName()} (ts, pvalue) values ('{data.Time}',{data.Value})";
    let query = format!("insert into `{}` values ('{}', {})", to_table_name(param.name), param.time, param.value);
    app_state.lock().await.td_engine_repo.exec_single(machine_index, query).await?;
    Ok(json_success_response("插入成功"))
}

pub async fn update_point(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Json(payload): Json<Value>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let param: PointDto = from_value(payload)?;
    let model = app_state.lock().await.point_repo.update_point_async(machine_index, param.base.id, param).await?;
    Ok(json_success_response(model))
}

pub async fn delete_point_by_id(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Query(idParam): Query<IdParam>,
) -> JsonResult<Value> {
    let machine_index = get_machine_index(headers);
    let id = idParam.id;
    app_state.lock().await.point_repo.delete_point_by_id_async(machine_index, id).await?;
    Ok(json_success_response("删除成功"))
}


#[derive(Deserialize, Clone)]
pub struct TableQueryParams {
    pub tableName: String,
    pub startTime: Option<String>,
    pub endTime: Option<String>,
    pub minValue: Option<f64>,
    pub maxValue: Option<f64>,
    pub pageNo: Option<usize>,
    pub pageSize: Option<usize>,
}

pub async fn get_point_table_page(
    State(app_state): State<Arc<Mutex<AppState>>>,
    headers: HeaderMap,
    Query(para): Query<TableQueryParams>,
) -> JsonResult<Value> {
    let offset = (para.pageNo.unwrap_or(1) - 1) * para.pageSize.unwrap_or(0);
    let mut query = format!("select * from `{}`", to_table_name(para.tableName.clone()));

    if let (Some(start_time), Some(end_time)) = (&para.startTime, &para.endTime) {
        query = format!("{} where ts >= '{}' and ts <= '{}'", query, start_time, end_time);
    }

    if let Some(min_value) = para.minValue {
        query = format!("{} and pvalue >= {}", query, min_value);
    }

    if let Some(max_value) = para.maxValue {
        query = format!("{} and pvalue <= {}", query, max_value);
    }

    if para.pageNo.is_some() && para.pageSize.is_some() {
        query = format!("{} limit {} offset {}", query, para.pageSize.unwrap(), offset);
    }

    info!("query list is {}", query);

    let machine_index = get_machine_index(headers);
    let ret = app_state.lock().await.td_engine_repo.query(machine_index, query).await?;

    // 获取总数
    let statistic = get_total_count_by_table_name(para.clone(), State(app_state.clone()), machine_index).await?;
    Ok(json_success_response(json!({
        "list": ret,
        "total": statistic.0,
        "avg": statistic.1,
        "min": statistic.2,
        "max": statistic.3,
        "minTs": statistic.4,
        "maxTs": statistic.5
    })))
}

async fn get_total_count_by_table_name(
    para: TableQueryParams,
    app_state: State<Arc<Mutex<AppState>>>,
    machine_index: u32,
) -> Result<(i64, f64, f64, f64, i64, i64), anyhow::Error> {
    let mut query = format!("select count(*) as total, avg(pvalue) as avgVal ,min(pvalue) as minVal, max(pvalue) as maxVal  from `{}`", to_table_name(para.tableName.clone()));

    if let (Some(start_time), Some(end_time)) = (&para.startTime, &para.endTime) {
        query = format!("{} where ts >= '{}' and ts <= '{}'", query, start_time, end_time);
    }

    if let Some(min_value) = para.minValue {
        query = format!("{} and pvalue >= {}", query, min_value);
    }

    if let Some(max_value) = para.maxValue {
        query = format!("{} and pvalue <= {}", query, max_value);
    }

    info!("query total count is {}", query);

    let res = app_state.lock().await.td_engine_repo.query(machine_index, query).await?;

    // 查询 min max value and specific time
    let mut min_query = format!("select min(pvalue) ,ts from `{}`", to_table_name(para.tableName.clone()));
    if let (Some(start_time), Some(end_time)) = (&para.startTime, &para.endTime) {
        min_query = format!("{} where ts >= '{}' and ts <= '{}'", min_query, start_time, end_time);
    }

    if let Some(min_value) = para.minValue {
        min_query = format!("{} and pvalue >= {}", min_query, min_value);
    }

    info!("query min pvalue  is {}", min_query);

    let min_res = app_state.lock().await.td_engine_repo.query(machine_index, min_query).await?;

    let mut max_query = format!("select max(pvalue) ,ts from `{}`", to_table_name(para.tableName.clone()));
    if let (Some(start_time), Some(end_time)) = (&para.startTime, &para.endTime) {
        max_query = format!("{} where ts >= '{}' and ts <= '{}'", max_query, start_time, end_time);
    }

    if let Some(min_value) = para.minValue {
        max_query = format!("{} and pvalue >= {}", max_query, min_value);
    }

    info!("query max pvalue  is {}", max_query);

    let max_res = app_state.lock().await.td_engine_repo.query(machine_index, max_query).await?;

    let total = res.get(0).and_then(|v| v.as_i64()).unwrap_or(0);
    let avg = res.get(1).and_then(|v| v.as_f64()).unwrap_or(0.0);
    let min_val = res.get(2).and_then(|v| v.as_f64()).unwrap_or(0.0);
    let max_val = res.get(3).and_then(|v| v.as_f64()).unwrap_or(0.0);
    let min_ts = min_res.get(1).and_then(|v| v.as_i64()).unwrap_or(0);
    let max_ts = max_res.get(1).and_then(|v| v.as_i64()).unwrap_or(0);

    Ok((total, avg, min_val, max_val, min_ts, max_ts))
}
